import sys
import os
import configparser
import requests
import re
import pymysql
import json
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
from binascii import b2a_hex, a2b_hex
import hashlib
import time
import psutil
import execjs
from bs4 import BeautifulSoup
import base64
from flask import Flask, request, jsonify, make_response
import threading
import random
import urllib3
urllib3.disable_warnings()
from datetime import timedelta, datetime
from multiprocessing import Queue
import traceback
import logging
from inspect import isfunction
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
from Crypto import Random
from lib.logger import Logger
from lib.client import Client
# from lib.test import heartBeat
from lib.api import Api
from lib.server import apiServer

# ZIP加密密钥
SAFE_ZIP_PROTECT = True
key = b'PLOCN35JKL0OEPQB'

# 常量定义
CONFIG_FILE_NAME = 'config.ini'

# 防止抓包调试模式
FAKE_DEBUG = True

proxies = {
    'http': None,
    'https': None
} if FAKE_DEBUG else {}

MAX_QUEUE_COUNT = 3
q = Queue(MAX_QUEUE_COUNT)

def myPut(val):
    global MAX_QUEUE_COUNT
    global q

    if q.qsize() == MAX_QUEUE_COUNT:
        q.get()

    q.put(val)


def config(key, cast = str):
    global cfg

    t = key.split('.')
    if len(t) != 2:
        return None
    
    (section, key) = t

    if not cfg.has_section(section):
        return None

    if not cfg.has_option(section, key):
        return None

    value = cfg.get(section, key)
    return cast(value)


def unix_time(dt):
    # 转换成时间数组
    timeArray = time.strptime(dt, "%Y-%m-%d %H:%M:%S")
    # 转换成时间戳
    timestamp = int(time.mktime(timeArray))
    return timestamp


uaMode = None

def getUA(scene):
    global logger
    global uaMode
    global q
    global loginJSContext

    start = time.process_time()
    try:
        if uaMode == None:
            uaMode = config('setting.ua-mode')

        logger.debug('获取UA, 模式: {}'.format(uaMode))

        if uaMode == 'js':
            if scene == 'login':
                return loginJSContext.call('e', '')
            else:
                return q.get()
        else:
            resp = requests.post(config('setting.ua-request-url'), data={
                'scene': scene
            }, proxies=proxies)
            resp = resp.json()
            if resp['code'] != 0:
                raise Exception(resp['msg'] if 'msg' in resp else 'UA请求异常')
            ua =  resp['result']

        logger.debug('UA值为: {}'.format(ua))
        return ua
    finally:
        logger.debug('获取UA, 耗时{}秒'.format(time.process_time() - start))


def heartBeat(client):
    global logger
    logger.info('心跳包服务已开启')

    while True:
        client.heartBeat()
        if not client.isLogin:
            logger.info('账号未处于登录状态, 已退出心跳')
            
            global account
            global password
            t3 = threading.Thread(target = loginThread, args=(client, account, password))
            t3.setDaemon(True)
            t3.start()
            break
        time.sleep(random.randint(3, 6))

def startHeartBeat(client):
    global logger
    t2 = threading.Thread(target = heartBeat, args=(client,))
    t2.setDaemon(True)
    t2.start()

def loginThread(client, account, password):
    global logger
    checkCode = ''

    client.openLoginPage()

    # 校验账号

    # 登录动作
    result = client.login(account, password, checkCode)
    if result == True:
        client.s.get('https://mrchportalweb.alipay.com/user/home.htm', verify=client.verify, proxies=proxies)
        startHeartBeat(client)


def uaServer():
    global formJSContext

    while True:
        myPut(formJSContext.call('e', ''))
        time.sleep(0.1)

if __name__ == "__main__":
    try:
        if not os.path.exists(CONFIG_FILE_NAME):
            raise FileNotFoundError(CONFIG_FILE_NAME)

        cfg = configparser.ConfigParser()
        cfg.read(CONFIG_FILE_NAME, encoding='utf-8-sig')

        logger = Logger(
            filename = config('logger.filename'),
            level = config('logger.level'),
            when = config('logger.when'),
            backCount = config('logger.back-count', int)
        )

        userAgent = config('browser.user-agent')

        logger.info('程序启动中')


        # 获取最新的环境信息
        if SAFE_ZIP_PROTECT:
            if not os.path.exists('env.js.zip'):
                raise FileNotFoundError('env.js.zip文件不存在')
            with open('env.js.zip', mode='rb') as f:
                zipBinData = f.read()

            dataLength = int.from_bytes(zipBinData[22:22+4], byteorder='big') >> 2

            # 检验数据包长度是否异常
            if dataLength != len(zipBinData):
                raise Exception('文件损坏')

            iv = bytes(reversed(zipBinData[26 : 26 + 8] + zipBinData[dataLength - 8 : dataLength]))

            ct_bytes = zipBinData[34 : dataLength - 8]
            cipher = AES.new(key, AES.MODE_CBC, iv)

            try:
                ct_bytes = unpad(cipher.decrypt(ct_bytes), AES.block_size)
                env = ct_bytes.decode()
            except Exception:
                raise Exception('文件损坏')
        else:
            if not os.path.exists('env.js'):
                raise FileNotFoundError('env.js文件不存在')
            with open('env.js', mode='r', encoding='utf-8') as f:
                env = f.read()

        # 初始化虚拟环境（登录）
        resp = requests.get('https://rds.alipay.com/ua_authcenter_login.js', headers={'User-Agent': userAgent}, verify=False, proxies=proxies)
        result = re.findall(r'https://.*\.js', resp.text)
        if len(result) == 0:
            raise Exception('检测到脚本更新, 无法正确提取到js脚本')
        jsCacheContent = requests.get(result[0], headers={'User-Agent': userAgent}, verify=False, proxies=proxies).text
        jsScript = env + '\n'
        jsScript += '''
        location.href = 'https://login.taobao.com/member/login.jhtml?from=taobaoindex&f=top&style=&sub=true&redirect_url=https%3A%2F%2Fmyseller.taobao.com%2Fhome.htm';
        document.referrer = 'https://myseller.taobao.com/home.htm'
        document.body.clientHeight = 295;
        document.body.clientWidth = 286;
        document.cookie = '';
        window.origin = 'https://login.taobao.com';
        window.innerHeight = 346;
        window.innerWidth = 286;
        window.outerHeight = 1040;
        window.outerWidth = 1920;
        window.Notification.permission = 'denied';
        '''
        jsScript += jsCacheContent
        jsScript += '\n'
        jsScript += '''
        function e(cookie) {
            document.cookie = cookie;
            var sx = parseInt(Math.random() * window.innerWidth / 2);
            var sy = parseInt(Math.random() * window.innerHeight / 2);
            var cx = parseInt(sx + Math.random() * (window.innerWidth - sx + 1), 10);
            var cy = parseInt(sy + Math.random() * (window.innerHeight - sy + 1), 10);
            var eventDown = document.createEvent('MouseEvents');
            eventDown.initMouseEvent('mousedown', true, true, window, 0, sx, sy, cx, cy, false, false, false, false, 0, null);  
            var eventUp = document.createEvent('MouseEvents');
            eventUp.initMouseEvent('mouseup', true, true, window, 0, sx, sy, cx, cy, false, false, false, false, 0, null);  
            document.body.dispatchEvent(eventDown);
            document.body.dispatchEvent(eventUp);
            return window.json_ua.toString();
        }
        '''
        loginJSContext = execjs.compile(jsScript)


        
        # 初始化虚拟环境（表单）
        resp = requests.get('https://rds.alipay.com/ua_excashier_edeposit.js', headers={'User-Agent': userAgent}, verify=False, proxies=proxies)
        result = re.findall(r'https://.*\.js', resp.text)
        if len(result) == 0:
            raise Exception('检测到脚本更新, 无法正确提取到js脚本')
        jsCacheContent = requests.get(result[0], headers={'User-Agent': userAgent}, verify=False, proxies=proxies).text
        jsScript = env + '\n'
        jsScript += '''
        location.href = 'https://myseller.taobao.com/home.htm';
        document.referrer = 'https://myseller.taobao.com/home.htm';
        document.cookie = '';
        document.body.clientHeight = 601;
        document.body.clientWidth = 1903;
        window.origin = 'https://myseller.taobao.com';
        window.innerHeight = 969;
        window.innerWidth = 1920;
        window.outerHeight = 1040;
        window.outerWidth = 1920;
        window.Notification.permission = 'default';
        '''
        jsScript += jsCacheContent
        jsScript += '\n'
        jsScript += '''
        function e(cookie) {
            document.cookie = cookie;
            var sx = parseInt(Math.random() * window.innerWidth / 2);
            var sy = parseInt(Math.random() * window.innerHeight / 2);
            var cx = parseInt(sx + Math.random() * (window.innerWidth - sx + 1), 10);
            var cy = parseInt(sy + Math.random() * (window.innerHeight - sy + 1), 10);
            var eventDown = document.createEvent('MouseEvents');
            eventDown.initMouseEvent('mousedown', true, true, window, 0, sx, sy, cx, cy, false, false, false, false, 0, null);  
            var eventUp = document.createEvent('MouseEvents');
            eventUp.initMouseEvent('mouseup', true, true, window, 0, sx, sy, cx, cy, false, false, false, false, 0, null);  
            document.body.dispatchEvent(eventDown);
            document.body.dispatchEvent(eventUp);
            return window.json_ua.toString();
        }
        '''
        formJSContext = execjs.compile(jsScript)



        t4 = threading.Thread(target = uaServer, args=())
        t4.setDaemon(True)
        t4.start()

    
        conn = pymysql.connect(
            config('mysql.host'),
            port = config('mysql.port', int),
            user = config('mysql.username'),
            passwd = config('mysql.password'),
            db = config('mysql.database')
        )

        account = config('taobao.account')
        password = config('taobao.password')
        username = config('api.username')
        apikey = config('api.apikey')
        apiProxy = Api(username, apikey, config('api.report-request-url'), logger)

        client = Client(conn, getUA, logger,config)
        client.setApiProxy(apiProxy)
        client.setUserAgent(userAgent)

        client.instance()


        # 启动服务监听
        '''
        t1 = threading.Thread(target = apiServer, args=(client, apiProxy, logger, config))
        t1.setDaemon(True)
        t1.start()
        '''
        # 心跳
        if client.isLogin:
            client.s.get('https://myseller.taobao.com/home.htm', verify=client.verify, proxies=proxies)
            startHeartBeat(client)
        else:
            t3 = threading.Thread(target = loginThread, args=(client, account, password))
            t3.setDaemon(True)
            t3.start()

        logger.info('启动完毕, 正在监听中')

        while True:
            time.sleep(0.1)

    except Exception as e:
        traceback.print_exc()
        # s = sys.exc_info()
        # logger.error("Error '%s' happened on line %d" % (s[1],s[2].tb_lineno))
